﻿namespace Spectral
{
    using System;
    using Spectral.Operations;

    /// <summary>
    /// Provides creational methods for rectangles.
    /// </summary>
    public static class Rectangle
    {
        /// <summary>
        /// Creates a rectangle.
        /// </summary>
        /// <typeparam name="T">The element type</typeparam>
        /// <param name="x">The x position.</param>
        /// <param name="y">The y position.</param>
        /// <param name="width">The width.</param>
        /// <param name="height">The height.</param>
        /// <returns>A rectangle.</returns>
        public static Rectangle<T> Create<T>(T x, T y, T width, T height)
            where T : struct, IEquatable<T>, IComparable<T>
        {
            return new Rectangle<T>(x, y, width, height);
        }

        /// <summary>
        /// Creates a rectangle.
        /// </summary>
        /// <typeparam name="T">The element type</typeparam>
        /// <param name="position">The position.</param>
        /// <param name="size">The size.</param>
        /// <returns>A rectangle.</returns>
        public static Rectangle<T> Create<T>(Vector2<T> position, Vector2<T> size)
            where T : struct, IEquatable<T>, IComparable<T>
        {
            return new Rectangle<T>(position, size);
        }

        /// <summary>
        /// Creates a rectangle from the specified sides.
        /// </summary>
        /// <typeparam name="T">The element type</typeparam>
        /// <param name="left">The left side of the rectangle.</param>
        /// <param name="right">The right side of the rectangle.</param>
        /// <param name="top">The top side of the rectangle.</param>
        /// <param name="bottom">The bottom side of the rectangle.</param>
        /// <returns>A rectangle.</returns>
        public static Rectangle<T> FromSides<T>(T left, T right, T top, T bottom)
            where T : struct, IEquatable<T>, IComparable<T>
        {
            return new Rectangle<T>(left, top, right.Subtract(left), bottom.Subtract(top));
        }
    }

    /// <summary>
    /// Represents a rectangle structure of user-defined type.
    /// </summary>
    /// <typeparam name="T">The element type.</typeparam>
    public struct Rectangle<T> : IEquatable<Rectangle<T>>
        where T : struct, IEquatable<T>, IComparable<T>
    {
        /// <summary>
        /// The x coordinate.
        /// </summary>
        public readonly T X;

        /// <summary>
        /// The y coordinate.
        /// </summary>
        public readonly T Y;

        /// <summary>
        /// The width.
        /// </summary>
        public readonly T Width;

        /// <summary>
        /// The height.
        /// </summary>
        public readonly T Height;

        /// <summary>
        /// Gets the position.
        /// </summary>
        public Vector2<T> Position { get { return new Vector2<T>(X, Y); } }

        /// <summary>
        /// Gets the size.
        /// </summary>
        public Vector2<T> Size { get { return new Vector2<T>(Width, Height); } }

        /// <summary>
        /// Gets the left side of the rectangle.
        /// </summary>
        public T Left { get { return X; } }

        /// <summary>
        /// Gets the right side of the rectangle.
        /// </summary>
        public T Right { get { return X.Add(Width); } }

        /// <summary>
        /// Gets the top side of the rectangle.
        /// </summary>
        public T Top { get { return Y; } }

        /// <summary>
        /// Gets the bottom side of the rectangle.
        /// </summary>
        public T Bottom { get { return Y.Add(Height); } }

        /// <summary>
        /// Initializes a new instance of the <see cref="Rectangle&lt;T&gt;"/> struct.
        /// </summary>
        /// <param name="x">The x.</param>
        /// <param name="y">The y.</param>
        /// <param name="width">The width.</param>
        /// <param name="height">The height.</param>
        public Rectangle(T x, T y, T width, T height)
        {
            X = x;
            Y = y;
            Width = width;
            Height = height;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Rectangle&lt;T&gt;"/> struct.
        /// </summary>
        /// <param name="position">The position.</param>
        /// <param name="size">The size.</param>
        public Rectangle(Vector2<T> position, Vector2<T> size)
        {
            X = position.X;
            Y = position.Y;
            Width = size.X;
            Height = size.Y;
        }

        /// <summary>
        /// Casts this instance.
        /// </summary>
        /// <typeparam name="U">The new element type.</typeparam>
        /// <returns>A rectangle.</returns>
        public Rectangle<U> Cast<U>()
            where U : struct, IEquatable<U>, IComparable<U>
        {
            return new Rectangle<U>((U)Convert.ChangeType(X, typeof(U)), (U)Convert.ChangeType(Y, typeof(U)), (U)Convert.ChangeType(Width, typeof(U)), (U)Convert.ChangeType(Height, typeof(U)));
        }

        /// <summary>
        /// Indicates whether the current object is equal to another object of the same type.
        /// </summary>
        /// <param name="other">An object to compare with this object.</param>
        /// <returns>
        /// true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
        /// </returns>
        public bool Equals(Rectangle<T> other)
        {
            return X.Equals(other.X) && Y.Equals(other.Y) && Width.Equals(other.Width) && Height.Equals(other.Height);
        }

        /// <summary>
        /// Determines whether the specified <see cref="System.Object"/> is equal to this instance.
        /// </summary>
        /// <param name="obj">The <see cref="System.Object"/> to compare with this instance.</param>
        /// <returns>
        ///   <c>true</c> if the specified <see cref="System.Object"/> is equal to this instance; otherwise, <c>false</c>.
        /// </returns>
        public override bool Equals(object obj)
        {
            return obj is Rectangle<T> ? Equals((Rectangle<T>)obj) : false;
        }

        /// <summary>
        /// Returns a hash code for this instance.
        /// </summary>
        /// <returns>
        /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. 
        /// </returns>
        public override int GetHashCode()
        {
            return X.GetHashCode() ^ Y.GetHashCode() ^ Width.GetHashCode() ^ Height.GetHashCode();
        }

        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            return string.Format("X: {0}, Y: {1}, Width: {2}, Height: {3}", X, Y, Width, Height);
        }
    }
}
